home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr48 / pas_0593.zip / WRITEXEC.PAS < prev   
Pascal/Delphi Source File  |  1993-05-30  |  10KB  |  265 lines

  1. {─ Fido Pascal Conference ────────────────────────────────────────────── PASCAL ─
  2. Msg  : 348 of 460
  3. From : Jon Jasiunas                        1:273/216.0          14 May 93  14:49
  4. To   : Dan Egli
  5. Subj : Config info in .EXE   1/3
  6. ────────────────────────────────────────────────────────────────────────────────
  7. ===========================================================================
  8. Date: 05-09-93 (19:46)
  9. Subj: Config info in .EXE
  10.    ------------------------------------------------------------------------
  11. DE> How are you saaving the CFG into the .EXE?? Mind posting some code that wil
  12.   > save the CFG to the EXE?(When you get all your bugs fixed!)
  13.  
  14. I use these routines in my self-modifying .EXE's. They work pretty good.
  15.  
  16.    - cut here ----}
  17. unit WritExec;
  18.  
  19.   { ==================================================================
  20.  
  21.                                Unit: WritExec
  22.                              Author: David Doty
  23.                                      Skipjack Software
  24.                                      Columbia, Maryland
  25.                CompuServe User I.D.: 76244,1043
  26.  
  27.     This unit is based on a previously published program:
  28.  
  29.                             Program: AutoInst v2.0
  30.                              Author: David Dubois
  31.                                      Zelkop Software
  32.                                      Halifax, Nova Scotia
  33.                CompuServe User I.D.: 71401,747
  34.                   Date last revised: 1988.04.24
  35.  
  36.     ==================================================================
  37.  
  38.     This source code is released to the public domain.  If further changes
  39.     are made, please include the above credits in the distributed code.
  40.  
  41.     This unit allows a program to change the value of a typed constant in its
  42.     own .EXE file.  When the program is run again, the data will be initialized
  43.     to the new value.  No external configuration files are necessary.
  44.  
  45.     USES
  46.  
  47.     Examples of the usefulness of this technique would be:
  48.  
  49.     o   A program that allows the user to change default display colors.
  50.  
  51.     o   A program that keeps track of a password that the user can change.
  52.  
  53.     HOW IT WORKS
  54.  
  55.     You don't have to understand all the details in order to use this
  56.     technique, but here they are.
  57.  
  58.     The data to be changed must be stored in a TurboPascal typed
  59.     constant.  In all effect, a typed constant is actually a pre-
  60.     initialized variable.  It is always stored in the program's Data
  61.     Segment.  The data can be of any type.
  62.  
  63.     First, the procedure finds the .EXE file by examining the DOS command
  64.     line, stored with the copy of the DOS environment for the program.  This
  65.     allows the program to find itself no matter where is resides on disk and
  66.     no matter how its name is changed by the user.
  67.  
  68.     The untyped file is opened with a record size of 1. This allows us
  69.     to read or write a string of bytes using BlockRead and BlockWrite.
  70.  
  71.     As documented in the DOS Technical Reference, the size of the .EXE
  72.     header, in paragraphs (a paragraph is 16 bytes), is stored as a
  73.     two-byte word at position 8 of the file.  This is read into the
  74.     variable HeaderSize.
  75.  
  76.     The next step is to find the position of the typed constant in the
  77.     .EXE file. This requires an understanding of the Turbo Pascal 4.0
  78.     memory map, documented on the first and second pages of the Inside
  79.     Turbo Pascal chapter. (That's chapter 26, pages 335 and 336 in my
  80.     manual.)
  81.  
  82.     First, find the address in memory where the typed constant is
  83.     stored. This can be done in Turbo Pascal by using the Seg and Ofs
  84.     functions. Next find the segment of the PSP (program segment
  85.     prefix). This should always be the value returned by PrefixSeg.
  86.     That will mark the beginning of the program in memory. The
  87.     position of the typed constant in the .EXE image should be the
  88.     number of bytes between these two places in memory. But ...
  89.  
  90.     But, two corrections must be made. First, the PSP is not stored in
  91.     the .EXE file. As mentioned on page 335, the PSP is always 256
  92.     bytes. We must subtract that out. Secondly, there is the .EXE file
  93.     header. The size of this has already been read in and must be
  94.     added in to our calculations.
  95.  
  96.     Once the position has been determined, the data stored in the
  97.     typed constant is written in one fell swoop using a BlockWrite.
  98.     This replaces the original data, so that the next time the program
  99.     is run, the new values will used.
  100.  
  101.     LIMITATIONS
  102.  
  103.     You cannot use MicroSoft's EXEPACK on the .EXE file, or any other
  104.     packing method I know of. This may change the position, or even
  105.     the size of the typed constant in the file image.
  106.  
  107.     NOTES
  108.  
  109.     Since typed constants are always stored in the data segment, the
  110.     function call to Seg( ObjectToWrite ) can be replaced with DSeg. I
  111.     prefer using Seg since it is more descriptive.
  112.  
  113.     One might think that Cseg can used as an alternative to using
  114.     PrefixSeg and subtracting 256. This will work only if the code
  115.     resides in the main program. If, on the other hand, the code is
  116.     used in a unit, PrefixSeg must be used as described here. You
  117.     might as well use PrefixSeg and save yourself some headaches.
  118.  
  119.     If you have any comments or questions we would be glad to hear
  120.     them. If you're on CompuServe, you can EasyPlex a letter to
  121.     76244,1043 or 71401,747. Or leave a message on the Borland Programmer's A
  122.     Forum (GO BPROGA). Or, you can write to
  123.  
  124.                          Skipjack Software
  125.                          P. O. Box 61
  126.                          Simpsonville Maryland 21150
  127.  
  128.                             or
  129.  
  130.                          Zelkop Software
  131.                          P.O. Box 5177
  132.                          Armdale, N.S.
  133.                          Canada
  134.                          B3L 4M7
  135.  
  136.     ==================================================================}
  137.  
  138.  
  139. INTERFACE
  140.  
  141. FUNCTION GetExecutableName : STRING;
  142. {  This function returns the full drive, path, and file name of the application
  143.    program that is running.  This function is of more general interest than
  144.    just for writing into the EXE file.
  145.  
  146.    NOTE: THIS FUNCTION WILL ONLY WORK UNDER DOS 3.X + !!! }
  147.  
  148. FUNCTION WriteToExecutable( { input } VAR ObjectToWrite;
  149.                             { input } ObjectSize : WORD ) : INTEGER;
  150. {  This procedure modifies the EXE file on disk to contain changes to typed
  151.    constants.  NOTE - the object MUST be a typed constant.  It may be found
  152.    in any part of the program (i.e., main program or any unit).  The call is
  153.    made by untyped address, to allow any kind of object to be written.  The
  154.    function returns the DOS error code from the I/O operation that failed
  155.    (if any did); if all operations were successful, the function returns 0. }
  156.  
  157. IMPLEMENTATION
  158.  
  159. FUNCTION GetExecutableName : STRING;
  160. TYPE
  161.    Environment = ARRAY[ 0..32766 ] OF CHAR;
  162. CONST
  163.    NullChar : Char = #0;
  164.    SearchFailed = $FFFF;
  165. VAR
  166.    MyEnviron : ^Environment;
  167.    Loop : WORD;
  168.    TempWord : WORD;
  169.    EnvironPos : WORD;
  170.    FilenamePos : WORD;
  171.    TempString : STRING;
  172. BEGIN { FUNCTION GetExecutableName }
  173.  
  174.    { Get pointer to DOS environment }
  175.    MyEnviron := Ptr( MemW[ PrefixSeg:$2C ], 0 );
  176.  
  177.    { Look for end of environment }
  178.  
  179.    EnvironPos := SearchFailed;
  180.    Loop := 0;
  181.    WHILE Loop <= 32767 DO BEGIN
  182.       IF MyEnviron^[ Loop ] = NullChar
  183.       THEN IF MyEnviron^[ Loop + 1 ] = NullChar
  184.          THEN BEGIN { found two nulls - this is end of environment }
  185.             EnvironPos := Loop;
  186.             Loop := 32767
  187.          END { found two nulls };
  188.       Inc( Loop )
  189.    END { WHILE Loop };
  190.    IF EnvironPos = SearchFailed
  191.    THEN GetExecutableName := ''
  192.    ELSE BEGIN { found end of environment - now look for path/file of exec }
  193.  
  194.       EnvironPos := EnvironPos + 4;
  195.       FilenamePos := SearchFailed;
  196.       TempWord := EnvironPos;
  197.       Loop := 0;
  198.       WHILE Loop <= 127 DO BEGIN
  199.          IF MyEnviron^[ TempWord ] = NullChar
  200.          THEN BEGIN { found a null - this is end of path/file of exec }
  201.             FilenamePos := Loop;
  202.             Loop := 127
  203.          END { found a null };
  204.          Inc( Loop );
  205.          Inc( TempWord )
  206.       END { WHILE Loop };
  207.       IF FilenamePos = SearchFailed
  208.       THEN GetExecutableName := ''
  209.       ELSE BEGIN { found executable name - move into return string }
  210.         TempString[ 0 ] := Chr( FilenamePos );
  211.         Move( MyEnviron^[ EnvironPos ], TempString[ 1 ], FilenamePos );
  212.         GetExecutableName := TempString
  213.       END { found executable name }
  214.  
  215.    END { found environment end }
  216.  
  217. END { FUNCTION GetExecutableName };
  218.  
  219.  
  220. FUNCTION WriteToExecutable( { input } VAR ObjectToWrite;
  221.                             { input } ObjectSize : WORD ) : INTEGER;
  222. CONST
  223.    PrefixSize = 256; { number of bytes in the Program Segment Prefix }
  224. VAR
  225.    Executable : FILE;
  226.    HeaderSize : WORD;
  227.    ErrorCode : INTEGER;
  228.  
  229. BEGIN { FUNCTION WriteToExecutable }
  230.    Assign( Executable, GetExecutableName );
  231.    {$I-} Reset( Executable, 1 );
  232.    ErrorCode := IOResult;
  233.  
  234.    IF ErrorCode = 0
  235.    THEN BEGIN { seek position of header size in EXE file }
  236.       Seek( Executable, 8 );
  237.       ErrorCode := IOResult
  238.    END { seek header };
  239.  
  240.    IF ErrorCode = 0
  241.    THEN BEGIN { read header size in EXE file }
  242.       BlockRead( Executable, HeaderSize, SizeOf( HeaderSize ) );
  243.       ErrorCode := IOResult
  244.    END { read header };
  245.  
  246.    IF ErrorCode = 0
  247.    THEN BEGIN { seek position of object in EXE file }
  248.       Seek( Executable,
  249.             LONGINT( 16 ) * ( HeaderSize + Seg( ObjectToWrite ) - PrefixSeg ) +
  250.             Ofs( ObjectToWrite ) - PrefixSize );
  251.       ErrorCode := IOResult
  252.    END { Seek object position in file };
  253.  
  254.    IF ErrorCode = 0
  255.    THEN BEGIN { write new password in EXE file }
  256.       BlockWrite( Executable, ObjectToWrite, ObjectSize );
  257.       ErrorCode := IOResult
  258.    END { write new password };
  259.  
  260.    Close( Executable );
  261.    WriteToExecutable := ErrorCode
  262.  
  263. END { FUNCTION WriteToExecutable };
  264.  
  265. END { UNIT WritExec }.